`timescale 1 ns/ 1 ns
module UDP_Send(
	CLK			,
	Rst_n			,
	Tx_Done		,
	
	des_mac		,
	src_mac		,
	
	des_port		,
	src_port		,
	
	des_ip		,
	src_ip		,
	
	data_length	,

	GMII_GTXC	,
	GMII_TXD		,
	GMII_TXEN	,
	
	wrreq			,
	wrdata		,
	wrclk			,
	aclr			,
	wrusedw		
);

	input CLK;
	input Rst_n;
	output reg Tx_Done;
	input [47:0] des_mac	;
	input [47:0] src_mac	;
	input [15:0] des_port;
	input [15:0] src_port;
	input [31:0] des_ip	;
	input [31:0] src_ip	;
	
	input [15:0] data_length;
	
	input GMII_GTXC;
	output reg [7:0] GMII_TXD;
	output reg GMII_TXEN;
	
	input wrreq;
	input [7:0]wrdata;
	input wrclk;
	input aclr;
	output [11:0] wrusedw;
	
	//参数定义
	localparam 	FRAME_TYPE 			= 16'h0800;
	localparam 	IDLE 					=	9'b000000001,
					SEND_HEADER 		=	9'b000000010, 
					SEND_DES_MAC 		=	9'b000000100,
					SEND_SRC_MAC		=	9'b000001000,
					SEND_FRAME_TYPE	=	9'b000010000,
					SEND_IP_HEADER		=	9'b000100000,
					SEND_UDP_HEADER	=	9'b001000000,	
					SEND_DATA			=	9'b010000000,	
					SEND_CRC				=	9'b100000000;
	
	
	// reg
	reg [47:0] des_mac_reg	;
   reg [47:0] src_mac_reg	;
   reg [15:0] des_port_reg	;
   reg [15:0] src_port_reg	;
   reg [31:0] des_ip_reg	;
   reg [31:0] src_ip_reg	;
	reg [15:0] data_length_reg;
	reg [7:0] GMII_TXD_reg;
	reg GMII_TXEN_reg;
	reg Go;
	reg [4:0] cnt_header; //前导码计数器
	reg [4:0] cnt_des_mac;//目标mac地址计数器
	reg [4:0] cnt_src_mac;//源mac地址计数器
	reg [1:0] cnt_frame_type;//帧类型计数器
	reg [4:0] cnt_ip_header;//IP报头计数器
	reg [4:0] cnt_udp_header;//UDP报头计数器
	reg [11:0]cnt_data;	//数据计数器
	reg [4:0] cnt_crc; //crc计数器
	reg [31:0] ip_header [4:0];
	reg [31:0] udp_header[1:0];
	reg [9:0]state;
	reg fifo_rdreq;
	reg [1:0] ctrl_state;
	reg [7:0] delay_cnt;
	reg CRC_EN;
	//wire
	wire [31:0] crc_result;
	wire [15:0] ip_checksum;
	wire [7:0] fifo_rddata;
	wire [12:0] rdusedw;
	wire [31:0] suma,sumb;
	wire CRC_Reset;
	//assign
	assign suma = 	ip_header[0][31:16] + ip_header[0][15:0] + 
						ip_header[1][31:16] + ip_header[1][15:0] +
						ip_header[2][31:16] + ip_header[3][31:16]+
						ip_header[3][15:0] + ip_header[4][31:16] + ip_header[4][15:0];	
	assign sumb = suma[31:16] + suma[15:0];
	assign ip_checksum = sumb[31:16]?~(sumb[31:16] + sumb[15:0]) : ~sumb;
	assign CRC_Reset = (state == IDLE);
	
	
	
	//将所有输入信号存储进入寄存器
	always @ (posedge GMII_GTXC)
	if (Go)
		begin
			des_mac_reg		 <= des_mac;			
		   src_mac_reg		 <= src_mac;
		   des_port_reg	 <= des_port;		
		   src_port_reg	 <= src_port;
		   des_ip_reg		 <= des_ip;	
		   src_ip_reg		 <= src_ip;	
			data_length_reg <= data_length;
		end
		
	//对输出端口加一级寄存器，以方便使用IO输出寄存器
	always @ (posedge GMII_GTXC)
	begin
		GMII_TXD  <= GMII_TXD_reg ;
		GMII_TXEN <= GMII_TXEN_reg;
	end
	
	
	eth_dcfifo eth_dcfifo_inst(
		.aclr			(aclr),
		.data			(wrdata),
		.rdclk		(GMII_GTXC),
		.rdreq		(fifo_rdreq),
		.wrclk		(wrclk),
		.wrreq		(wrreq),
		.q				(fifo_rddata),
		.rdusedw		(rdusedw),
		.wrusedw		(wrusedw)
	);
	
	always @ (posedge GMII_GTXC or negedge Rst_n)
	if (!Rst_n)
		begin
			ctrl_state <= 2'd0;
			Go <= 1'b0;
			delay_cnt <= 8'd0;
		end
	else
		begin
			case (ctrl_state)
				2'd0	:	begin
								if (rdusedw >= data_length)
									begin
										Go <= 1'b1;
										ctrl_state <= 2'd1;
									end
								else
									begin
										Go <= 1'b0;
										ctrl_state <= 2'd0;								
									end
							end
							
				2'd1	:	begin
								Go <= 1'b0;
								if (Tx_Done)
									ctrl_state <= 2'd2;
								else
									ctrl_state <= 2'd1;
							end
							
				2'd2	:	begin
								if (delay_cnt == 8'd255)
									begin
										ctrl_state <= 2'd3;
										delay_cnt <= 8'd0;
									end
								else
									begin
										ctrl_state <= 2'd2;
										delay_cnt <= delay_cnt + 1'b1;
									end
							end
							
				2'd3	:	begin
								ctrl_state <= 2'd0;
							end
				default : ctrl_state <= 2'd0;
			endcase
		end

	always @ (posedge GMII_GTXC )
	begin
		ip_header[0][31:24] <= 8'h45; //协议版本+首部长度
		ip_header[0][23:16] <= 8'h00; //服务类型 
		ip_header[0][15:0]  <= data_length_reg + 8'd28; // ip报头+数据
		ip_header[1][31:0]  <= 32'd0; //数据包标识+分段偏移
		ip_header[2][31:24]  <= 8'h40;//生存周期
		ip_header[2][23:16] <=  8'h11;	//UDP协议
		ip_header[2][15:0] <= ip_checksum;	//IP 校验和
		ip_header[3][31:0] <= src_ip_reg;	//源IP地址	
		ip_header[4][31:0] <= des_ip_reg;	//目的IP地址
	end

	always @ (posedge GMII_GTXC)
	begin
		udp_header[0][31:16] <= src_port_reg;	//源端口号
		udp_header[0][15:0] <= des_port_reg;	//目的端口号
		udp_header[1][31:16] <= data_length_reg + 8'd8;	//UDP报头+数据
		udp_header[1][15:0] <= 16'h00;	//UDP报头校验和  忽略
	end

	CRC32_D8	CRC32_D8_inst(
		.Clk			(GMII_GTXC),
		.Reset		(CRC_Reset),
		.Data_in		(GMII_TXD_reg), 
		.Enable		(CRC_EN),
		.Crc			(),
		.CrcNext		(),
		.Crc_eth		(crc_result)
	);
	
	always @ (posedge GMII_GTXC or negedge Rst_n)
	if (!Rst_n)
		begin
			state <= IDLE;
			cnt_header <= 5'd0;
			cnt_des_mac <= 5'd0;
			cnt_src_mac <= 5'd0;
			cnt_frame_type <= 2'd0;
			cnt_ip_header <= 5'd0;
			cnt_udp_header <= 5'd0;
			cnt_data	<= 12'd0;
			cnt_crc <= 5'd0;
			GMII_TXD_reg  <= 8'd0;
	      GMII_TXEN_reg <= 1'b0;
			fifo_rdreq <= 1'b0;
			CRC_EN <= 1'b0;
			Tx_Done <= 1'b0;
		end
	else
		begin
			case (state)
				IDLE				:	begin
											GMII_TXEN_reg <= 1'b0;
											Tx_Done <= 1'b0;
											if (Go)
												state <= SEND_HEADER;
											else
												state <= IDLE;
										end
								
				SEND_HEADER		:	begin
											GMII_TXEN_reg <= 1'b1;
											if (cnt_header >= 7) 
												begin
													cnt_header <= 5'd0;
													state <= SEND_DES_MAC;
												end
											else
												begin
													state <= SEND_HEADER;
													cnt_header <= cnt_header + 1'b1;
												end
											case (cnt_header)
												0,1,2,3,4,5,6	:	GMII_TXD_reg <= 8'h55;
												7 :	GMII_TXD_reg <= 8'hd5;
												default:GMII_TXD_reg <= 8'h55;
											endcase
										end
										
				SEND_DES_MAC	:	begin
											CRC_EN <= 1'b1;
											if (cnt_des_mac >= 5'd5)
												begin
													state <= SEND_SRC_MAC;
													cnt_des_mac <= 5'd0;
												end
											else
												begin
													state <= SEND_DES_MAC;
													cnt_des_mac <= cnt_des_mac + 1'b1;
												end
											case (cnt_des_mac)
												0	:	GMII_TXD_reg <= des_mac_reg[47:40];
												1	:	GMII_TXD_reg <= des_mac_reg[39:32];
												2	:	GMII_TXD_reg <= des_mac_reg[31:24];
												3	:	GMII_TXD_reg <= des_mac_reg[23:16];
												4	:	GMII_TXD_reg <= des_mac_reg[15:8];
												5	:	GMII_TXD_reg <= des_mac_reg[7:0];
												default : GMII_TXD_reg <= 8'hff;
											endcase
										end
										
				SEND_SRC_MAC	:	begin
											if (cnt_src_mac >= 5)
												begin
													state <= SEND_FRAME_TYPE;
													cnt_src_mac <= 5'd0;	
												end
											else
												begin
													state <= SEND_SRC_MAC;
													cnt_src_mac <= cnt_src_mac + 1'b1;
												end
											case (cnt_src_mac)
												0	:	GMII_TXD_reg <= src_mac_reg[47:40];
												1	:	GMII_TXD_reg <= src_mac_reg[39:32];
												2	:	GMII_TXD_reg <= src_mac_reg[31:24];
												3	:	GMII_TXD_reg <= src_mac_reg[23:16];
												4	:	GMII_TXD_reg <= src_mac_reg[15:8];
												5	:	GMII_TXD_reg <= src_mac_reg[7:0];
												default : GMII_TXD_reg <= 8'hff;
											endcase
										end
									
				SEND_FRAME_TYPE:	begin
											if (cnt_frame_type >= 1)
												begin
													state <= SEND_IP_HEADER;
													cnt_frame_type <= 2'd0;
												end
											else
												begin
													state <= SEND_FRAME_TYPE;
													cnt_frame_type <= cnt_frame_type + 1'b1;
												end
											case (cnt_frame_type)
												0	:	GMII_TXD_reg <= FRAME_TYPE[15:8];
												1	:	GMII_TXD_reg <= FRAME_TYPE[7:0];
												default : GMII_TXD_reg <= 8'hff;
											endcase 
										end
										
				SEND_IP_HEADER	:	begin
											if (cnt_ip_header >= 19)
												begin
													state <= SEND_UDP_HEADER;
													cnt_ip_header <= 5'd0;
												end
											else
												begin
													state <= SEND_IP_HEADER;
													cnt_ip_header <= cnt_ip_header + 1'b1;
												end
											case (cnt_ip_header)
												0	:	GMII_TXD_reg <= ip_header[0][31:24];
												1	:	GMII_TXD_reg <= ip_header[0][23:16];
												2	:	GMII_TXD_reg <= ip_header[0][15:8];
												3	:	GMII_TXD_reg <= ip_header[0][7:0];
												
												4	:	GMII_TXD_reg <= ip_header[1][31:24];
												5	:	GMII_TXD_reg <= ip_header[1][23:16];
												6	:	GMII_TXD_reg <= ip_header[1][15:8];
												7	:	GMII_TXD_reg <= ip_header[1][7:0];
												
												8	:	GMII_TXD_reg <= ip_header[2][31:24];
												9	:	GMII_TXD_reg <= ip_header[2][23:16];
												10	:	GMII_TXD_reg <= ip_header[2][15:8];
												11	:	GMII_TXD_reg <= ip_header[2][7:0];
												
												12	:	GMII_TXD_reg <= ip_header[3][31:24];
												13	:	GMII_TXD_reg <= ip_header[3][23:16];
												14	:	GMII_TXD_reg <= ip_header[3][15:8];
												15	:	GMII_TXD_reg <= ip_header[3][7:0];
												
												16	:	GMII_TXD_reg <= ip_header[4][31:24];
												17	:	GMII_TXD_reg <= ip_header[4][23:16];
												18	:	GMII_TXD_reg <= ip_header[4][15:8];
												19	:	GMII_TXD_reg <= ip_header[4][7:0];
												default : GMII_TXD_reg <= 8'hff;
											endcase
										end
										
				SEND_UDP_HEADER:	begin
											if (cnt_udp_header >= 7)
												begin
													state <= SEND_DATA;
													fifo_rdreq <= 1'b1;
													cnt_udp_header <= 5'd0;
												end
											else
												begin
													state <= SEND_UDP_HEADER;
													cnt_udp_header <= cnt_udp_header + 1'b1;
												end
											case (cnt_udp_header)
												0	:	GMII_TXD_reg <= udp_header[0][31:24];
												1	:	GMII_TXD_reg <= udp_header[0][23:16];
												2	:	GMII_TXD_reg <= udp_header[0][15:8];
												3	:	GMII_TXD_reg <= udp_header[0][7:0];
												
												4	:	GMII_TXD_reg <= udp_header[1][31:24];
												5	:	GMII_TXD_reg <= udp_header[1][23:16];
												6	:	GMII_TXD_reg <= udp_header[1][15:8];
												7	:	GMII_TXD_reg <= udp_header[1][7:0];
												default : GMII_TXD_reg <= 8'hff;
											endcase
										end
										
				SEND_DATA		:	begin
											if (cnt_data >= data_length_reg - 1)
												begin
													fifo_rdreq <= 1'b0;
													state <= SEND_CRC;
													cnt_data <= 12'd0;
													GMII_TXD_reg <= fifo_rddata;
												end
											else
												begin
													state <= SEND_DATA;
													cnt_data <= cnt_data + 1'b1;
													GMII_TXD_reg <= fifo_rddata;
												end
										end
										
					SEND_CRC		:	begin
											CRC_EN <= 1'b0;
											if (cnt_crc >= 3)
												begin
													state <= IDLE;
													cnt_crc <= 5'd0;
													Tx_Done <= 1'b1;
												end
											else	
												begin
													state <= SEND_CRC;
													cnt_crc <= cnt_crc + 1'b1;
												end
											case (cnt_crc)
												0	:	GMII_TXD_reg <= crc_result[31:24];
												1	:	GMII_TXD_reg <= crc_result[23:16];
												2	:	GMII_TXD_reg <= crc_result[15:8];
												3	:	GMII_TXD_reg <= crc_result[7:0];
												default : GMII_TXD_reg <= 8'hff;
											endcase 
										end
				default : state <= IDLE;
			endcase
		end

endmodule 